#include "logo_lib.h"
#include "logo_lib_p.h"
#include "interpret.h"
#include <windows.h>
#include <assert.h>


static BkPtNode* find_breakpoint (const BkPtList list, unsigned int lineno);
static void remove_breakpoint (BkPtList* list, const BkPtNode* node);
static int add_breakpoint (BkPtList* list, unsigned int lineno);
static void debug_bkpt_insert_line (rte_t* rte, unsigned int lineno);
static void debug_bkpt_remove_line (rte_t* rte, unsigned int lineno);

tagDebugControl::tagDebugControl() 
{
   memset (this, 0, sizeof (tagDebugControl));
}

tagDebugControl::~tagDebugControl()
{
   BkPtNode* cur = breakpoints;
   while (cur) {
      BkPtNode* next = cur->next;
      free ((void*) cur);
      cur = next;
   }
}

tagExecControl::tagExecControl()
{
   Aborted = false;
   Paused = false;
   Debug = false;
   ThreadID = 0;
}

tagExecControl::~tagExecControl()
{
}

void user_abort(exe_handle_t hexe, bool abort)
{
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex)
   rte->control.Aborted = abort;
   REL_MUTEX (rte->mutex)
}

void set_debug_mode(exe_handle_t hexe, bool debug)
{
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex)
   rte->control.Debug = debug;
   REL_MUTEX (rte->mutex)
}

bool get_debug_mode(exe_handle_t hexe)
{
   bool b;
   rte_t* rte = (rte_t*) hexe;
   assert (rte);
   GET_MUTEX (rte->mutex)
   b = rte->control.Debug;
   REL_MUTEX (rte->mutex)
   return b;
}

void save_thread_id(exe_handle_t hexe)
{
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex)
   rte->control.ThreadID = GetCurrentThreadId();
   REL_MUTEX (rte->mutex)
}

unsigned long get_thread_id(rte_t* rte)
{
   unsigned long id;
   assert (rte);
   GET_MUTEX (rte->mutex)
   id = rte->control.ThreadID;
   REL_MUTEX (rte->mutex)
   return id;
}

bool get_abort (rte_t* rte)
{
   bool ret;
   assert (rte);
   GET_MUTEX (rte->mutex)
   ret = rte->control.Aborted;
   REL_MUTEX (rte->mutex)
   return ret;
}

void set_pause (exe_handle_t hexe, bool paused)
{
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex)
   rte->control.Paused = true;
   REL_MUTEX (rte->mutex)
}

bool get_pause (exe_handle_t hexe)
{
   bool ret;
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex)
   ret = rte->control.Paused;
   REL_MUTEX (rte->mutex)
   return ret;
}

static void debug_helper (rte_t* rte, unsigned int command, unsigned int lineno)
{
   assert (rte);
   if (!get_debug_mode(rte)) {
      return;
   }
   ::PostThreadMessage(get_thread_id(rte), command, lineno, 0);
   return;
}

void debug_start(exe_handle_t hexe)
{
   assert (hexe);
   debug_helper((rte_t*)hexe, MSG_DEBUG_START, 0);
}

void debug_go(exe_handle_t hexe)
{
   assert (hexe);
   debug_helper((rte_t*)hexe, MSG_DEBUG_GO, 0);
}

void debug_step_over(exe_handle_t hexe)
{
   assert (hexe);
    debug_helper((rte_t*)hexe, MSG_DEBUG_STEP_OVER, 0);
}

void debug_step_into(exe_handle_t hexe)
{
   assert (hexe);
    debug_helper((rte_t*)hexe, MSG_DEBUG_STEP_INTO, 0);
}

void debug_step_out(exe_handle_t hexe)
{
   assert (hexe);
    debug_helper((rte_t*)hexe, MSG_DEBUG_STEP_OUT, 0);
}

void debug_step_to(exe_handle_t hexe, unsigned int lineno)
{
   assert (hexe);
    debug_helper((rte_t*)hexe, MSG_DEBUG_STEP_TO, lineno);
}

void debug_stop(exe_handle_t hexe)
{
   assert (hexe);
    debug_helper((rte_t*)hexe, MSG_DEBUG_STOP, 0);
}

void debug_toggle_breakpoint(exe_handle_t hexe, unsigned int lineno)
{
	BkPtNode*	bkpt;
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex);
      bkpt = find_breakpoint(rte->control.DbgCtrl.breakpoints, lineno);
	   if (bkpt) {
		   remove_breakpoint(&(rte->control.DbgCtrl.breakpoints), bkpt);
	   } else {
		   add_breakpoint(&(rte->control.DbgCtrl.breakpoints), lineno);
	   }
   REL_MUTEX (rte->mutex)
	return;
}

void debug_free_breakpointlist(exe_handle_t hexe)
{
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex);
      BkPtNode* cur = rte->control.DbgCtrl.breakpoints;
      while (cur) {
         BkPtNode* next = cur->next;
         free ((void*) cur);
         cur = next;
      }
      rte->control.DbgCtrl.breakpoints = NULL;
   REL_MUTEX (rte->mutex)
}

void debug_adjust_breakpoints(exe_handle_t hexe, bool insert, unsigned int lineno)
{
   assert (hexe);
   if (insert) {
      debug_bkpt_insert_line ((rte_t*)hexe, lineno);
   } else {
      debug_bkpt_remove_line ((rte_t*)hexe, lineno);
   }
}

bool debug_find_breakpoint (exe_handle_t hexe, unsigned int lineno)
{
   BkPtNode* ret = NULL;
   
   assert (hexe);
   rte_t* rte = (rte_t*) hexe;
   GET_MUTEX (rte->mutex);
      ret = find_breakpoint(rte->control.DbgCtrl.breakpoints, lineno);
   REL_MUTEX (rte->mutex)
   return (ret != NULL );
}

void get_debug_cmd(rte_t* rte)
{
   assert (rte);
   MSG msg;

   msg.message = 0;
   GetMessage(&msg, NULL, MSG_DEBUG_FIRST, MSG_DEBUG_LAST);
   rte->control.DbgCtrl.command = msg.message;
   rte->control.DbgCtrl.lineno = msg.wParam;
   rte->control.DbgCtrl.waitfornext = false;
}

static BkPtNode* find_breakpoint (const BkPtList list, unsigned int lineno)
{
	BkPtNode* ret = list;
	while (ret) {
		if (ret->lineno == lineno) {
			return ret;
		}
		ret = ret->next;
	}
	return NULL;
}

static void remove_breakpoint (BkPtList* list, const BkPtNode* node)
{
	assert (list);
	assert (node);

	BkPtNode* cur = *list;
	if (cur == node) {
		*list = node->next;
		free ((void*)node);
		return;
	}

	while (cur && cur->next != node) {
		cur = cur->next;
	}
	assert (cur && cur->next == node);
	cur->next = node->next;
	free ((void*)node);
}

static int add_breakpoint (BkPtList* list, unsigned int lineno)
{
	assert (list);

	BkPtNode* node = (BkPtNode*) malloc (sizeof (BkPtNode));
	if (!node) {
		return -1;
	}
	node->lineno = lineno;
	node->next = *list;
	*list = node;
	return 0;
}

static void debug_bkpt_insert_line (rte_t* rte, unsigned int lineno)
{
   assert (rte);
   GET_MUTEX (rte->mutex);
      BkPtNode* node = rte->control.DbgCtrl.breakpoints;
      while (node) {
         if (node->lineno >= lineno) {
            (node->lineno)++;
         }
         node = node->next;
      }
   REL_MUTEX (rte->mutex)
}

static void debug_bkpt_remove_line (rte_t* rte, unsigned int lineno)
{
   assert (rte);
   GET_MUTEX (rte->mutex);
      BkPtNode* head = rte->control.DbgCtrl.breakpoints;
      if (head && head->lineno == lineno) {
         rte->control.DbgCtrl.breakpoints = head->next;
         free ((void*) head);
      }
      BkPtNode* cur = rte->control.DbgCtrl.breakpoints;
      while (cur) {
         BkPtNode* next = cur->next;
         assert (cur->lineno != lineno);
         if (cur->lineno > lineno) {
            (cur->lineno)--;
         }
         if (next && next->lineno == lineno) {
            cur->next = next->next;
            free ((void*)next);
         }
         cur = cur->next;
      }
   REL_MUTEX (rte->mutex)
}
